iT邦幫忙

2024 iThome 鐵人賽

DAY 3
0

每天的專案會同步到 GitLab 上,可以前往 GitLab 查看。
有興趣的朋友歡迎留言 or 來信討論,我的信箱是 nickchen1998@gmail.com

本日大綱

  • 手動調閱紀錄
  • LangChain 內建方法

手動調閱紀錄

一般來說,要讓語言模型呈現類似記憶力的效果,其實就是把過往的對話紀錄在提問的時候一並傳遞給模型。這樣模型就可以根據過往的對話內容來回答問題。
可以看到昨天的文章當中,我們在調用語言模型回答時,傳遞了一個 list 型態的參數,裡面有 SystemMessage 以及 HumanMessage,我們可以透過查詢資料庫,
把過往的對話紀錄放置於這個地方,並在 list 的尾巴街上我們想問的問題,來達到傳遞對話紀錄的效果。

下面我們先利用 SQAlchemy 來建立一個簡單的資料庫,並將對話紀錄存入資料庫中,通常我們在設計對話紀錄的資料庫時,會在資料表當中設計一個欄位,
用來把所有的對話紀錄分組,也方便進行查詢,這邊我是透過一個叫做 nanoid 的套件來進行,針對 SQLAlchemy 比較不熟悉的朋友可以參考 這個網址

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import declarative_base, sessionmaker
from nanoid import generate

# 定義模型
Base = declarative_base()


class ChatLog(Base):
    __tablename__ = 'chat_log'
    id = Column(Integer, primary_key=True, autoincrement=True)
    nano_id = Column(String)
    question = Column(String)
    answer = Column(Integer)


if __name__ == '__main__':
    engine = create_engine('sqlite:///example.db', echo=True)
    Base.metadata.create_all(engine)

    Session = sessionmaker(bind=engine)
    session = Session()

    session.add(ChatLog(
        nano_id=generate(), 
        question="我是 nick,我是一名軟體工程師。", 
        answer="收到,你好,我是 ChatGPT。"
    ))
    session.commit()

接著我們就來一步一步把對話紀錄丟給模型並進行問答,首先我們先匯入必要的套件:

from env_settings import EnvSettings, BASE_DIR
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
from sqlalchemy import create_engine, select
from sqlalchemy.orm import sessionmaker
from model import ChatLog

接著我們要透過查詢資料庫把對話紀錄取出來:

env_settings = EnvSettings()

engine = create_engine(f"sqlite:///{BASE_DIR / 'example.db'}", echo=True)
session = sessionmaker(bind=engine)()
stmt = select(ChatLog).where(ChatLog.nano_id == "U26Wn16LS6aRrx6tUw8Ak")
chat_logs = session.execute(stmt).scalars().all()

再來我們要把對話紀錄轉換成 LangChain 的訊息格式,並在串列的尾巴使用 HumanMessage 放上我們想提問的問題:

messages = [SystemMessage(content="你是一個繁體中文機器人,會理解使用者的提問並使用繁體中文回答。")]
for chat_log in chat_logs:
    messages.append(HumanMessage(content=chat_log.question))
    messages.append(AIMessage(content=chat_log.answer))

messages.append(HumanMessage(content="你好,請問我是誰?"))

最後就可以使用 invoke 來調用模型進行問答並查看結果:

ai_message = llm.invoke(messages)
print(ai_message.content)

可以看到下圖中的執行結果,GPT 有成功識別出我們是誰,並給予正確的資訊:
執行結果

對話紀錄摘要

剛剛我們採取了手動調閱對話紀錄的方式,將一批對話紀錄傳遞給語言模型,這個方式的優點是,會保留最原始的對話紀錄,但通常語言模型會有一些 token 的
字數限制等,如果今天想要越過這個限制,或是想要加速一些效能的話,我們可以試著將對話紀錄做摘要,只保留對話紀錄的重點,這樣可以減少傳遞給語言模型的資料量。

LangChain 內建方法

而在 LangChain 當中,也幫我們封裝好了一個物件叫做 SQLChatMessageHistory 讓我們可以更快速的進行這項工作,不需要從設定資料表開始,當然如果
你需要更加客製化的製作 prompt 或是用了一寫比較稀有的資料庫等等的特殊情況,你也可以採用手動調閱 or 覆寫這個物件來進行。

讓我們快速看一下範例:

可以看到寫了一個 get_session_history 的方法,這個方法會回傳一個 SQLChatMessageHistory 物件,這個物件會幫我們把對話紀錄存入資料庫中,
我們也可以在這裡設定資料庫的連線字串等等,並不一定要使用 sqlite。

from env_settings import EnvSettings, BASE_DIR
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langchain_community.chat_message_histories import SQLChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory


def get_session_history(session_id):
    return SQLChatMessageHistory(
        session_id,
        f"sqlite:///{BASE_DIR / 'example.db'}"
    )


env_settings = EnvSettings()
llm = ChatOpenAI(
    api_key=env_settings.OPENAI_API_KEY,
    model_name="gpt-4o"
)

runner = RunnableWithMessageHistory(
    llm,
    get_session_history
)
ai_message = runner.invoke(
    [HumanMessage(content="你好,我是 nick")],
    config={"configurable": {"session_id": "1"}},
)
print(ai_message.content)

下圖是執行完成的結果,可以看到 langchain 協助我們在資料庫當中自己生出了一張表,並且依照我們給予的 session_id 建立了兩筆紀錄,
分別是問題以及回答。

生成資料表

內容預告

今天我們介紹了如何讓語言模型擁有記憶力,明天我們要來介紹在 langchain 當中,該如何下 prompt,幫助我們更加彈性的進行問答內容的編輯。


上一篇
Day 02 - 串接 GPT API 以及 langchain 常用的 message 類型
下一篇
Day 04 - Prompt Template 以及要點
系列文
初探 Langchain 與 LLM:打造簡易問診機器人30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言